from marshmallow import Schema,fields,validate,validates,ValidationError,post_load,validates_schema
from marshmallow_sqlalchemy import SQLAlchemyAutoSchema,auto_field
from flask import current_app

from .models import User
from application import message,db
from application.services import users_services

class MobileSchema(Schema):
    """手机号转换器"""
    mobile = fields.String(required=True, validate=validate.Regexp("^1[3-9]\d{9}$", error=message.mobile_format_error))
    @validates("mobile")
    def validate_mobile(self,data):
        """验证手机号是否已经被注册"""
        user = users_services.check_user_mobile(data)
        if user:
            raise ValidationError(message=message.mobile_is_use)

        return data

class UserSchema(SQLAlchemyAutoSchema):
    mobile = auto_field(required=True, load_only=True)
    password = fields.String(required=True, load_only=True)
    password2 = fields.String(required=True, load_only=True)
    sms_code = fields.String(required=True, load_only=True)

    class Meta:
        model = User
        include_fk = True  # 启用外键关系
        include_relationships = True  # 模型关系外部属性
        fields = ["id", "name", "mobile", "password", "password2", "sms_code"]  # 如果要全换全部字段，就不要声明fields或exclude字段即可
        sqla_session = db.session

    @post_load()
    def save_object(self, data, **kwargs):
        """保存用户基本信息"""
        data.pop("password2")
        data.pop("sms_code")
        data["name"] = current_app.faker.name()
        instance = users_services.save_user_data(data)
        return instance

    @validates_schema
    def validate(self, data, **kwargs):
        # 校验密码和确认密码
        if data["password"] != data["password2"]:
            raise ValidationError(message=message.password_not_match,field_name="password")

        # 1. 从redis中提取验证码
        try:
            redis_sms_code = users_services.get_data_by_cache("sms_%s" % data["mobile"])
        except users_services.DataNotExists as e:
            raise ValidationError(message=message.sms_code_expired, field_name="sms_code")

        # 2. 从客户端提交的数据data中提取验证码
        sms_code = data["sms_code"]
        # 3. 字符串比较，如果失败，则抛出异常，否则，直接删除验证码
        if sms_code != redis_sms_code:
            raise ValidationError(message=message.sms_code_not_match, field_name="sms_code")

        users_services.delete_data_by_cache("sms_%s" % data["mobile"])

        return data